grammar Lua;

options
{
	language=Java;
	output=AST;
}

@parser::header
{
	package lua.compiler.generated;
}

@lexer::header
{
	package lua.compiler.generated;
}

program
	: block
	;	

block
	: stmt* laststmt? // kazdy block moze ale nie musi konczyc sie return
	;

stmt
	: var ASSIGN^ exp // przypisanie 
	| functioncall // wywolanie funkcji
	| DEF^ NAME LPAREN! (paramlist)? RPAREN! block END!
	| IF^ exp block elseblock? END! //dzieki END nie ma problemu z dopasowywaniem ELSEow
	;

elseblock
	: ELSE^ block
	;
	
// ostatnia instrukcja w bloku, nie musi wystepowac
laststmt
	: RETURN^ exp? // mozemy cos zwracac, nie musimy
	;
	
//lista parametrow funkcji (dla definicji funkcji)
paramlist
	: NAME (COMMA! NAME)*
	;

//lista wyrazen (parametry funkcji)
explist
	: exp (COMMA! exp)*
	;

exp
	: relexp (logicop^ relexp)*
	;

relexp
	: plusexp (relop^ plusexp)*
	;
	
plusexp
	: mulexp (plusop^ mulexp)*
	;

mulexp
	: atom (mulop^ atom)*
	;

//wyrazenie
atom
	: number 
	| string
	| var 
	| functioncall
	| LPAREN! exp RPAREN! 
	| unop^ exp
	;
	
//zmienna, ma tylko nazwe
var
	: NAME^
	;

//wywolanie funkcji, moze cos zwracac ale nie musi
functioncall
	: CALL^ NAME LPAREN! args RPAREN!
	;

//lista argumentow wywolania funkcji
args
	: explist?
	;

// najnizszy priorytet
plusop
	: PLUS | MINUS | CONCAT
	;
	
mulop
	: MUL | DIV | MOD
	;

// najwyzszy priorytet
logicop
	: AND | OR;
	
relop
	: LT | LE 
	| GT | GE 
	| EQ | NEQ;

// operatory unarne - minus i negacja wartosci binarnych
unop
	: MINUS^
	| NOT^
	;

//rozne formaty liczb
number
	: INT^
	| FLOAT^ 
	| EXP^ 
	| HEX^
	;

string
	: STRING^ 
	;

// LEXER

//ponizsze stale nie musza byc zdefiniowane, ale zwiekszaja czytelnosc w kodzie

// slowa kluczowe
DO:	'do';
END: 'end';
IF: 'if';
THEN: 'then';
ELSE: 'else';
DEF: 'def';
RETURN: 'return';
BREAK: 'break';
NIL: 'nil';
CALL: 'call';

// operatory
ASSIGN: '=';
LPAREN: '(';
RPAREN: ')';
QUOT: '"';
COMMA: ',';
AND: 'and';
OR: 'or';
NOT: 'not';
PLUS: '+';
MINUS: '-';
MUL: '*';
DIV: '/';
MOD: '%';
LT: '<';
LE: '<=';
GT: '>';
GE: '>=';
EQ: '==';
NEQ: '!=';
CONCAT : '..';

//nazwa nie moze zaczynac sie od cyfry, reszta znakow moze byc cyfra/liczba/_
NAME
	: ('a'..'z'|'A'..'Z'|'_')(options{greedy=true;}:	'a'..'z'|'A'..'Z'|'_'|'0'..'9')*
	;

INT
	: ('0'..'9')+
	;

FLOAT
	: INT '.' INT
	;

// trzeba przeksztalcic na normalnie zapisanego floata, tzn. Float.parseFloat
EXP
	: (INT | FLOAT) ('E'|'e') ('-')? INT
	;

// liczba zapisana szesnastkowo - nie trzeba z nia nic robic, w CIL zapisujemy je tak samo
HEX
	: '0x' ('0'..'9'| 'a'..'f')+
	;

// zwykle stringi z mozliwymi \n, \t itd.
STRING
	: QUOT (~('\"'))* QUOT  
    ;

// wszystko co ponizej ignorujemy w kodzie
// komentarz wielolinijkowy
COMMENT
    :   '--[[' ( options {greedy=false;} : . )* ']]' {$channel = HIDDEN; }
    ;
    
// komentarz jednolinijkowy
LINE_COMMENT
    : '--' ~('\n'|'\r')* '\r'? '\n' {$channel = HIDDEN; }
    ;

// bialy znak
WS  :  (' '|'\t'|'\u000C') {$channel = HIDDEN; }
    ;
   
// nowa linia 
NEWLINE	: ('\r')? '\n' {$channel = HIDDEN; }
	;